(function(){
	//Global functions
	namespace = function(name, context){
		var values = name.split('.',2);
		if( !context ){
			context = global;
		}
		if( !context[values[0]] ){
			context[values[0]] = {}
		}
		context = context[values[0]];
		if( values.length == 2){
			namespace(name.substring(name.indexOf('.')+1), context);
		}
	}
	_recurseProperty = function(property, context){
		var values = property.split('.', 2);
		if(!context) return null;
		if(!context[values[0]]) return null;
		context = context[values[0]];
		if( values.length == 2){
			return _recurseProperty(property.substring(property.indexOf('.')+1), context);
		}
		return context;
	};
	propertyAccessor = function(property){
		return function(object){
			return _recurseProperty(property, object);
		}
	};
	_get = function(property){
		return {
			from : propertyAccessor(property)
		};
	};
	AutoCompleteResponse = function(id, results){
		this.results = results;
		this.values = [];
		var bases = [];
		var list = this.values;
		iterate(results, function(set){
			var filtered = filter(set.values, '^' + id, set._toString);
			List.addAll(list, filtered);
			var base = _gcs(filtered, set._toString);
			if( base )
				bases.push(base);
		}, this);
		this.base = _gcs(bases);
		this.toHTML = function(){
			var html = '';
			iterate(results, function(set){
				var filtered = filter(set.values, '^' + id, set._toString);
				iterate(filtered, function(item){
					if( !set.toHTML ) set.toHTML = "${0} ";
					html += format(set.toHTML, item);
				});
			});
			return html;
		}
	}
	
	chain = function(){
		return {
			items    : []
			,index   : 0
			,execute : function(callback){
				if( callback ){
					this.items.push({method:callback, scope:(callback.scope||this), arguments:[]});
				}
				var ctx = this.items[this.index];
				var scope = ctx.scope ? ctx.scope : this;
				ctx.method.apply(scope, ctx.arguments);
			}
			,proceed : function(){
				this.index++;
				if( this.index < this.items.length )
					this.execute();
			}
			,add : function(){
				if( arguments.length == 1 ){
					this.items.push(arguments[0]);
				}else{
					var ctx = {method:arguments[0], scope:(arguments[2]||this), arguments:arguments[1]};
					this.items.push(ctx);
				};
				return this;
			}
			,invoke : function(){
				this.add.apply(this, arguments);
			}
		}
	}
	
	merge = function(obj){
		return function(item, name){
			var data = {}
			for(property in item) data[property] = item[property];
			for(property in obj) data[property] = obj[property];
			return {key:name, value:data};
		}
	}
	
	namespace('Map')
	Map.putAll = function(map1, map2, converter){
		if(map2)
			iterate(map2, function(item, name){
				if( converter ){
					var pair = converter(item, name);
					map1[pair.key] = pair.value;
				}else{
					map1[name] = item;
				} 
			})
		return map1;
	}
	Map.keySet = function(map, converter){
		var keys = []
		if( map ){
			iterate(map, function(item, key){
				keys.push(converter? converter(key) : key);
			})
		}
		return keys;
	}
	Map.values = function(map, converter){
		var values = []
		if( map ){
			iterate(map, function(item, key){
				values.push(converter? converter(item) : item);
			})
		}
		return values;
	}
	
	namespace('List');
	List.addAll = function(list1, list2, converter){
		if(list2)
			iterate(list2, function(item){ list1.push(converter?converter(item):item); });
		return list1;
	}
	List.clean = function(list){
		var store = []
		iterate(list, function(item){store.push(item);});
	}
	
	iterate = function(list, callback, scope){
		if( !scope ) scope = this;
		if( !list ) return;
		if( typeof list == 'Array' || list.length || list.length === 0){
			for(var index=0; index < list.length; index++)
				callback.apply(scope, [list[index], index]);
		}else{
			for(var field in list){
				callback.apply(scope, [list[field], field]);
			}
		}
	};
	
	filter = function(collection, pattern, _toString){
		var results = [];
		iterate(collection, function(item){
			var str = item;
			if( _toString ) str = _toString(item);
			if( str && str.match(pattern) ) results.push(item);
		});
		return results;
	};
	_matchAll = function(collection, pattern, _toString){
		return filter(collection,pattern, _toString).length == collection.length;
	};
	_max = function(collection, _toString){
		var result;
		iterate(collection, function(item){
			if( !result ) result = item;
			if( _toString(item).length > _toString(result).length ) result = item;
		});
		return result;
	};
	_formatEach = function(collection, formatString, separator){
		var results = [];
		var _sep = '';
		iterate(collection, function(item){
			results.push(_sep + format(formatString, item) );
			if( !_sep && separator ) _sep = separator;
		});
		return results;
	};
	_combine = function(collection){
		var result = '';
		iterate(collection, function(item){ result += item; })
		return result;
	};
	_gcs = function(collection, _toString){
		if(collection.length == 0) return '';
		if(!_toString) _toString = function(item){ return item; }
		var max = _max(collection, _toString);
		var maxstr = _toString(max);
		var last = '';
		for(var i=1; i < maxstr.length; i++){
			var sub = maxstr.substring(0, i);
			if(!_matchAll(collection, '^' + sub, _toString))
				return last;
			last = sub;
		}
		return maxstr;
	};
	_ = function(){
		return format.apply(this, arguments);
	};
	_.out = function(){
		console.log(format.apply(this,arguments));
	}
	_debug = function(item){
		console.log(item);
	};
	_pretty = function(obj, indent){
		if( !obj ) return '';
		if( typeof obj === 'string' ) return "'<span>" + obj + "</span>'"
		if( typeof obj != 'object' ) return obj;
		if( obj.length ){
			var out = '';
			iterate(obj, function(element){
				if( out ) out += indent + ',';
				else out += '['
				out += _pretty(element, indent);	
			})
			out += ']';
			return out;
		}
		if( !indent ) indent = '';
		var findent = indent + '&nbsp;&nbsp;&nbsp;';
		var out = '{<br/>';
		var lead = '';
		for(property in obj){
			if(!obj[property]) continue;
			out += findent + lead + property + ' : ' + _pretty(obj[property], findent) +'<br/>';
			lead = ','
		}
		out += indent + '}';
		return '' + out;
	};
	
	format = function(message){
		var result = message;
		var variables = message.match(/[$]{[a-zA-Z0-9\[\]\.$_]*}/g)
		var incoming = arguments;
		iterate(variables, function(variable){
			var name = variable.substring(2, variable.length-1);
			if( name.match(/^[0-9]*$/) ){
				var i = parseInt(name);
				result = result.replace(variable, trimQuote(_pretty(incoming[i+1])));
			}
			else{
				try{
					var value = _get(name).from(incoming[1]);
					var tmp = JSON.stringify(value);
					tmp = trimQuote(tmp || value);
					result = result.replace(variable, tmp);
				}catch(e){
					console.log(e);
				}
			}
		});
		return result;
	};
	format.out = function(){
		console.log(format.apply(this,arguments));
	};
	
	toArray = function(string, delimiter, escape){
		if( !delimiter ) delimiter = ' ';
		var commandAndArgs = string.split(delimiter);
		var _args = [];
		if( commandAndArgs.length >= 1 ){
			var tmp = '';
			iterate(commandAndArgs, function(arg, index){
				if( arg.match(/^['"]/) ){
					tmp = arg;
				}else if( arg.match(/['"]$/) ){
					tmp += " " + arg;
					_args.push(tmp);
					tmp = '';
				}else if( !tmp ){
					_args.push(arg);
				}else{
					tmp += " " + arg;
				}
			});
			if( tmp ) _args.push(tmp);
		}
		return _args;
	};
	
	trimQuote = function(value){
		if( !value ) return value;
		if( value.match(/^['"]/) ) value = value.substring(1);
		if( value.match(/['"]$/) ) value = value.substring(0, value.length-1);
		return value.trim();
	}
	
})();